using System.Net.NetworkInformation;
using System.Text;
using MQTTnet;
using MQTTnet.Client;

class Program
{

    const string iot_discovery_prefix = "homeassistant/button/desktop";

    static async Task Main(string[] args)
    {
        var mac = GetMacAddress();
        if (mac == null)
        {
            Console.WriteLine("No network interface is available.");
            return;
        }

        var topic = $"{iot_discovery_prefix}/{mac}";

        var factory = new MqttFactory();
        var mqttClient = factory.CreateMqttClient();

        var options = new MqttClientOptionsBuilder()
            .WithClientId(mac)
            .WithTcpServer("MQTT服务地址", 1883)
            .WithCredentials("你的客户端登录用户名", "你的密码")
            // 遗嘱消息
            .WithWillTopic($"{topic}/availability")
            .WithWillPayload("offline")
            .WithWillQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce)
            .WithCleanSession()
            .Build();


        mqttClient.ConnectedAsync += MqttClient_ConnectedAsync;

        mqttClient.DisconnectedAsync += MqttClient_DisconnectedAsync;

        mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync;

        await mqttClient.ConnectAsync(options);

        // 订阅主题 接收命令
        await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic($"{topic}/commands").Build());


        // 发布发现消息
        var discoveryMessage = new
        {
            name = "我的电脑",
            command_topic = $"{topic}/commands",
            unique_id = $"pc_{mac}",
            availability = new
            {
                topic = $"{topic}/availability",
            }
        };
        var payload = Encoding.UTF8.GetBytes(System.Text.Json.JsonSerializer.Serialize(discoveryMessage));
        Console.WriteLine($"Discovery message: {Encoding.UTF8.GetString(payload)}");

        var message = new MqttApplicationMessageBuilder()
            .WithTopic($"{topic}/config")
            .WithPayload(payload)
            .Build();

        await mqttClient.PublishAsync(message);

        // 发布在线消息
        await mqttClient.PublishAsync(new MqttApplicationMessageBuilder()
            .WithTopic($"{topic}/availability")
            .WithPayload("online")
            .WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce)
            .WithRetainFlag()
            .Build());

        Console.WriteLine("Press any key to exit.");
        Console.ReadLine();

        await mqttClient.DisconnectAsync();
    }

    private static Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
    {
        var topic = arg.ApplicationMessage.Topic;
        var payload = Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment);
        
        Console.WriteLine($"Message received on topic: {topic}.");
        Console.WriteLine($"Payload: {payload}");

        if (topic.EndsWith("commands"))
        {
            // 按钮按下
            if (payload == "PRESS")
            {
                Console.WriteLine("Shutting down...");
                // System.Diagnostics.Process.Start("shutdown", "/s /t 0");
                System.Diagnostics.Process.Start("shutdown", "/s");
                Environment.Exit(0);
                // 可以取消关机 shutdown /a
            }
        }

        return Task.CompletedTask;
    }

    private static Task MqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
    {
        Console.WriteLine("Disconnected from EMQX.");
        return Task.CompletedTask;
    }

    private static Task MqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
    {
        Console.WriteLine("Connected to EMQX.");
        return Task.CompletedTask;
    }


    /// <summary>
    /// 获取第一个可用的网卡的MAC地址
    /// </summary>
    /// <returns></returns>
    static string? GetMacAddress()
    {
        return NetworkInterface.GetAllNetworkInterfaces()
            .Where(nic => nic.OperationalStatus == OperationalStatus.Up)
            .Select(nic => nic.GetPhysicalAddress().ToString())
            .FirstOrDefault();
    }
}